from simpledcpp.core.base import DCPPManager, start_new_thread, DCPPPart, DCPPEvent, ST_SOCKET_TM, with_psyco
from simpledcpp.core.base import DCPPTCPSocket as Socket
from simpledcpp.core.dctools import PacketProcessor,createKey
from simpledcpp.core.connections.prepare_connection import PrepareMe
import socket, os
import time,sys
class ConnectInfo:
	def __init__(self, nickname, password = None, infointerest = "", infospeed = "", infoemail = "", infosharesize = None):
		self.nickname = nickname
		self.infointerest = infointerest
		self.infospeed = infospeed
		self.infoemail = infoemail
		self.infosharesize = infosharesize
		self.password = password

class HubConnection(DCPPPart):
	def __init__(self, parent):
		self.datatypes = {
		1 : ("any"),
		2 : ("mp3", "mp2", "wav", "au", "rm", "mid", "sm"),
		3 : ("zip", "arj", "rar", "lzh", "gz", "z", "arc", "pak"),
		4 : ("doc", "txt", "wri", "pdf", "ps", "tex"),
		5 : ("pm", "exe", "bat", "com"),
		6 : ("gif", "jpg", "jpeg", "bmp", "pcx", "png", "wmf", "psd"),
		7 : ("mpg", "mpeg", "avi", "asf", "mov"),
		8 : ('____FOLDER____'),
		9 : ('')
		}

		DCPPPart.__init__(self, parent)
		self.Events.Subscribe("ON_TASK_REQUEST", self._requestTask)
		self.Events.Subscribe("ON_SEARCH_REQUEST", self._searchrequest)
		self.Events.Subscribe("PRIVATE_MESSAGE", self._private)
		self.Events.Subscribe("MESSAGE_TO", self._messageto)

	def _messageto(self, Event):
		_hub, _nickname, _text = Event.userdata
		try:
			if _hub == self._hub:
				if self.settings.get('codepage','cp1251') != 'utf8':
					_text = self.from_utf8(_text, self.settings.get('codepage','cp1251'))
				self._socket.Send("$To: %s From: %s $<%s> %s|"%(_nickname, self._connectInfo.nickname, self._connectInfo.nickname, _text))
		except:
			print sys.exc_info()
	def _private(self, Event):
		_hub, text = Event.userdata
		if self.settings.get('codepage','cp1251') != 'utf8':
			text = self.from_utf8(text, self.settings.get('codepage','cp1251'))
		try:
			if _hub == self._hub:
				self._socket.Send("<%s> %s|"%(self._connectInfo.nickname, text))
		except:
			pass

	def Connect(self, ConnectInfo, Hub, Port = 411):
		self._hub = Hub
		self._connectInfo = ConnectInfo
		self._socket = Socket(DEBUG = False)
		self.Event(DCPPEvent("ON_HUB_CONNECT_START", (Hub,)))
		self._socket.Connect((Hub, Port), TIMEOUT = 5)
		self._processor = PacketProcessor()
		self._processor.ClearBuffer()
		if not self._socket.ok():
			self.status = self._socket.status
			if self.status == ST_SOCKET_TM:
				why = "Connection timeout."
			else:
				why = "Connection broken."
			self.Event(DCPPEvent("ON_HUB_DISCONNECTED", (self._hub, why)))
			return False
                self.Event(DCPPEvent('ON_HUB_CONNECT_OK',(Hub,)))
		self.thread = start_new_thread(self.process)
		return True

	def process(self):
		while True:
			data = self._socket.Recieve()
			if not self._socket.ok():
				self.status = self._socket.status
				self.Event(DCPPEvent("ON_HUB_DISCONNECTED", (self._hub, "Socket error.")))
				return False
			if self.settings.get('codepage','cp1251') != 'utf8':
				data = self.to_utf8(data,self.settings.get('codepage','cp1251'))
			self._processor.Process(self._socket, data, self.command_handler)

	@with_psyco
	def command_handler(self, command, sock):
		a = command.split(' ')
                if a[0] == "$Lock":
	                self._lock = a[1]
			key = createKey(self._lock)
                	sock.Send("$Key %s|$ValidateNick %s|"%(key,self._connectInfo.nickname))
                        sock.Send("$Version %s|$GetNickList|$MyINFO $ALL %s %s$ $%s$%s$%s$|"%("1,0091",
                                                                                self._connectInfo.nickname,
                                                                                self._connectInfo.infointerest,
                                                                                self._connectInfo.infospeed + "\5",
                                                                                self._connectInfo.infoemail,
                                                                                self._connectInfo.infosharesize))
		elif a[0] == "$HubName":
			hub = command.split(' ',1)
			self.hubname = hub[1]
			self.Event(DCPPEvent('ON_HUB_NAME_RECIEVED', (self._hub, self.hubname)))
		elif a[0] == '$To:':
			#print command
			mes = command.split('$')
			to = mes[1].split(' ')[1]
			fm = mes[1].split(' ')[3]
			mg = mes[2]
			self.Event(DCPPEvent("ON_PRIVATE_MESSAGE", (self._hub, to, fm, mg)))

		elif a[0] == "$ValidateDenide":
			print "WARNING: Your nick is already used."
			self.Event(DCPPEvent("ON_HUB_DISCONNECTED", (self._hub, "Nick already used")))
			self.stop()

		elif a[0] == "$GetPass":
			print "Password required."
			if self._connectInfo.password == None:
				print "Password undefined. Disconnect."
				sock.Close()
			sock.Send("$MyPass %s|"%self._connectInfo.password)
			#May be info ?
		elif a[0] == "$BadPass":
			print "Password don't match. Disconnect."
			self.Event(DCPPEvent("ON_HUB_DISCONNECTED",(self._hub, "Password don't match.")))
			self.stop()
		elif a[0] == "$LogedIn":
			print "Logged in."
		elif a[0] == "$MyINFO":
			# handle concatenated myinfos
			eachcommand = command.split('$MyINFO')
			for command in eachcommand:
				tmp = command.split('$')
				if tmp[0] == '':
					continue
				nickname = tmp[1].split(' ',2)[1]
				interest = tmp[1].split(' ',2)[2]
				speed = tmp[3].strip()[:-1]
				email = tmp[4]
				sharesize = tmp[5]
				self.Event(DCPPEvent("ON_USER_CONNECT",(self._hub, nickname, interest, speed, email, sharesize)))
		elif a[0] == '$Search':
			try:
				self._parseSearch(command)
			except:
				pass
			
		elif a[0] == '$HubTopic':
			pass
		elif a[0] == '$UserIP':
			print 'Ip of %s detected as %s'%(a[1],a[2])
		elif a[0] == '$Quit':
			self.Event(DCPPEvent("ON_USER_DISCONNECT",(self._hub, a[1],)))
		elif a[0] == '$Kick':
			self.Event(DCPPEvent("ON_USER_DISCONNECT",(self._hub, a[1])))
		elif a[0] == "$Hello":
			pass
		elif a[0] == '$ConnectToMe':
			if a[1] == self._connectInfo.nickname:# Edit this.
				b = a[2].split(":")
				nb = b[0].split("$$")[0]
				addr = (nb,int(b[1]))
				PrepareMe(self, addr)
			else:
				pass
		elif a[0] == "$NickList" or a[0] == "$OpList":
			nlist = command.split(' ',1)[1]
			nlist = nlist.split('$$')
			for i in nlist:
				if i!="":
					sock.Send("$GetINFO %s %s|"%(i, self._connectInfo.nickname))
		else:
			if not a[0:] == ['']:
				self.Event(DCPPEvent("ON_HUB_MESSAGE",(self._hub, command)))

	@with_psyco
	def _parseSearch(self, command):
		search = command.split(' ',2)
		ip, port = search[1].split(':')
		if ip == 'Hub':
			return
		addr = (ip, int(port))
		searchstring = search[2]
		issize,msize,size,datatype,searchpattern = searchstring.split('?',4)
		if msize == 'F':
			msize = False
		else:
			msize = True
		if issize == 'F':
			issize = False
		else:
			issize = True
		size = int(size)
		datatype = int(datatype)
		TTH_SEARCH = False
		if searchpattern.startswith("TTH:") and datatype == 9:
			TTH_SEARCH = True
			TTH = searchpattern.split(':')[1]
		else:
			terms = searchpattern.split('$')
		if TTH_SEARCH:
			if not self.filelist.getInfoByTTH(TTH):
				return
			else:
				self._foundTTH(self.filelist.getInfoByTTH(TTH), TTH, addr)
				return
		found = 0
		finfos = list()
		dirs = list()
		founddirs = 0
		for file in self.filelist.filelist:
			if founddirs + found > 13: break
			localfound = False
			for term in terms:
				try:
					filename = file.split('/')[-1]
					ind = filename.lower().index(term.lower())
					filesize = int(self.filelist.filelist[file][0])
#					print issize, msize, size, filesize
					if (issize and ( (msize and filesize > size) or (not msize and filesize < size) )) or not issize:
						ext = file.split('.')[-1]
						if ext in self.datatypes[datatype] or datatype == 1:
							tth = self.filelist.filelist[file][1]
							finfos.append((file.replace('/','\\'), filesize, tth))
							found = found + 1
							localfound = True
					
				except:
		#			print sys.exc_info()
					pass
				try:
					if datatype == 8 or datatype == 1:
						dirname = file.split('/')[:-1]
						dir_name = "/"
						for i in dirname:
							if i != "":
								dir_name = dir_name + i + '/'
						ind = dir_name.lower().index(term.lower())
						dirs.append(dir_name.replace('//','/').replace('/',"\\"))
						founddirs = founddirs + 1
						localfound = True
				except:
					pass
				if localfound: break
		if found + founddirs > 0:
			self._foundFile(finfos, dirs, addr)	

	def _foundTTH(self, finfo, TTH, addr):
		filename, filesize = finfo
		filename = filename.replace("/","\\")

		string = "$SR %s %s\5%s 5/%s\5TTH:%s (%s:%s)|"%(
		self._connectInfo.nickname,
		filename,
		filesize,
		self.settings.get("upload_slots", 10),
		TTH,
		self.settings.get("local_ip",'0.0.0.0'),
		self.settings.get("local_port",12321)
		)
		udpsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		udpsocket.connect(addr)
                if self.settings.get('codepage','cp1251') != 'utf8':
                	string = string.encode(self.settings.get('codepage','cp1251'))
                udpsocket.send(string)
                udpsocket.close()

	def _foundFile(self, finfos, founddirs, addr):
		strings = list()
		for i in finfos:
			filename, filesize, TTH = i
			strings.append("$SR %s %s\5%s 5/%s\5TTH:%s (%s:%s)|"%(
			self._connectInfo.nickname,
			filename,
			filesize,
			self.settings.get("upload_slots", 10),
			TTH,
			self.settings.get("local_ip",'0.0.0.0'),
			self.settings.get("local_port",12321)
			))
		alreadyIn = list()	
		for i in founddirs:
			string = "$SR %s %s 5/%s\5%s (%s:%s)|"%(
			self._connectInfo.nickname,
			i,
			self.settings.get('upload_slots', 10),
			self._hub,
			self.settings.get("local_ip",'0.0.0.0'),
			self.settings.get("local_port",12321)
			)
			if not string in alreadyIn:
				strings.append(string)
				alreadyIn.append(string)
		udpsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		udpsocket.connect(addr)
		for i in strings:
			t = i
			if self.settings.get('codepage','cp1251') != 'utf8':
				t = t.encode(self.settings.get('codepage','cp1251'))
			udpsocket.send(t)
		udpsocket.close()
	def _requestTask(self, Event):
		task = Event.userdata[0]
		src = Event.userdata[1]
		self._socket.Send("$ConnectToMe %s %s:%s|"%(src.nickname, self.settings.get('local_ip','0.0.0.0'),self.settings.get('local_port',12321)))
                time.sleep(0.5)

	def stop(self):
	#	DCPPPart.stop(self)
		try:
			self.thread.kill()
		except:
			pass
                self.Events.Unsubscribe("ON_TASK_REQUEST", self._requestTask)
                self.Events.Unsubscribe("ON_SEARCH_REQUEST", self._searchrequest)
                self.Events.Unsubscribe("PRIVATE_MESSAGE", self._private)
                self.Events.Unsubscribe("MESSAGE_TO", self._messageto)
		self._socket.Close()

	def SearchTTH(self, TTH):
		string = "$Search %s:%s F?T?0?9?TTH:%s|"%(self.settings.get('local_ip','0.0.0.0'),self.settings.get('local_port',12321), TTH)
		self._socket.Send(string)

	def _searchrequest(self, Event):
		sizer, ismin, ismax, datatype, terms = Event.userdata
		self.Search(sizer, ismin, ismax, datatype, terms)

	def Search(self, sizer, ismin, ismax, datatype, terms):
                if self.settings.get('codepage','cp1251') != 'utf8':
                        terms = self.from_utf8(terms, self.settings.get('codepage','cp1251'))
		term = terms.strip().replace(' ','$')
		request = "$Search %s:%s %s?%s?%s?%s?%s|"%(self.settings.get('local_ip','0.0.0.0'),
							self.settings.get('local_port',12321),
							sizer,
							ismin,
							ismax, 
							datatype,
							term)
#	print request
		self._socket.Send(request)
			

class HubManager(DCPPManager):
	def __init__(self, parent):
		DCPPManager.__init__(self, parent)
		self.Events.Subscribe("SEARCH_TTH", self._searchTTH)
	def Connect(self, ConnectInfo, Address):
		self.Events.Subscribe('ON_HUB_DISCONNECTED', self._ondisconnected)
		hubcon = HubConnection(self)
		if hubcon.Connect(ConnectInfo, Address[0], Address[1]):
			return self.add(hubcon)
		return False
	
	def Disconnect(self, ID):
		_hub = self.elements[ID]._hub
		try:
			self.elements[ID].stop()
		except:
			pass
		self.remove(ID)
		self.Event(DCPPEvent("ON_HUB_DISCONNECTED",(_hub, "Force disconnect.",)))

	def DisconnectFrom(self, From):
		ID = None
		for i in self.elements:
			if self.elements[i]._hub == From:
				ID = i
		if ID != None:
			self.Disconnect(ID)

	def stop(self):
		for i in self.elements:
			self.elements[i].stop()
	
	def _ondisconnected(self, Event):
		mustremove = None
		for i in self.elements:
			if i._hub == Event.userdata[0]:
				mustremove = i
		if mustremove:
			self.elements.remove(mustremove)

	def _searchTTH(self, Event):
		try:
			self.SearchTTH(Event.userdata[0])
		except:
			print sys.exc_info()

	def SearchTTH(self, TTH):
		for i in self.elements:
			self.elements[i].SearchTTH(TTH)

	def Search(self, sizer, ismin, ismax, datatype, terms):
		for i in self.elements:
			i.Search(sizer, ismin, ismax, datatype, terms)
